/**
 * 统一任务管理 - 用于单片机裸机的实现
 * - 定时任务 基于 shift_block_queue 实现
 * - 消息驱动的任务 基于 shift_block_queue 实现
 * - 信号驱动的任务 基于 主程序循环+全局变量 实现
 * 张勇 41204@qq.com
 */

#include <string.h>

#include "std_err.h"
#include "utils.h"
#include "calendar.h"
#include "unios.h"
#include "transceiver.h"
#include "shift_block_queue.h"


/// @brief 任务类型定义
typedef enum _unios_task_type {
	UNIOS_TASK_NULL = 0, 	///< 无效的任务
	UNIOS_TASK_SIGNAL,		///< 信号响应任务
	UNIOS_TASK_RX,		 	///< 接收数据处理任务
	UNIOS_TASK_TX,		 	///< 发送数据处理任务(单次)
	UNIOS_TASK_TXN,		 	///< 发送数据处理任务(多次), 有任务ID
	UNIOS_TASK_MS_TIMER, 	///< ms 级定时运行, 有任务ID
	UNIOS_TASK_RTC_TIMER, 	///< RTC 定时运行, 有任务ID
	UNIOS_TASK_MAX
} unios_task_type_t;


// 注意: 任务数据结构要压缩存储，字段间无空隙 !!!
#pragma pack(push, 1)

/// @brief 接收消息处理任务类型定义
typedef struct {
	void* receiver;					///< 数据接收者(是谁接收到的数据，具体含义由应用自行判断)
	unios_task_rxmsg_type_t msgType;///< 消息类型
	uint8_t data[4];   				///< 任务数据体，实际长度另行给出
} OSTaskRxMessage;

/// @brief (单次)发送消息处理任务类型定义
typedef struct {
	transceiver_t* sender;			///< 数据发送者(此数据要由谁发送)
	uint8_t data[4];   				///< 任务数据体，实际长度另行给出
} OSTaskTxMessage;

/// @brief (多次)发送消息处理任务类型定义
typedef struct {
	transceiver_t* sender;			///< 数据发送者(此数据要由谁发送)
	uint32_t interval;				///< 重复发送间隔时间(ms)
	uint32_t repeat;				///< 要重复发送的次数
	uint32_t lastSent;				///< 上一次发送是在什么时候(ms)
	unios_task_t id;				///< 任务ID
	uint8_t data[4];   				///< 任务数据体，实际长度另行给出
} OSTaskTxMessageXn;

/// @brief ms 级定时任务类型定义
typedef struct {
	unios_task_handler_t handler;	///< 任务回调函数
	uint32_t interval;				///< 重复触发间隔时间(ms)
	uint32_t repeat;				///< 要重复触发的次数
	uint32_t lastActive;			///< 上一次触发是在什么时候(ms)
	unios_task_t id;				///< 任务ID
	uint8_t data[4];   				///< 任务数据体，实际长度另行给出
} OSTaskMsTimer;

/// @brief RTC 定时任务类型定义
typedef struct {
	unios_task_handler_t handler;	///< 任务回调函数
	unios_sec_t time;				///< 每天执行的时间(每天的第几秒)
	uint32_t repeat;				///< 要重复触发的次数
	unios_sec_t lastActive;			///< 上一次执行是在什么时候(rtcsec), 如果距当前时间超过 24 小时就表示要执行了! 执行完成后需要更新 lastActive 为当前时间!
	unios_task_t id;				///< 任务ID
	uint8_t data[4];   				///< 任务数据体，实际长度另行给出
} OSTaskRtcTimer;

/// @brief 信号响应任务类型定义
typedef struct {
	unios_task_handler_t handler;	///< 任务回调函数
	unios_signal_t signal: 8;		///< 任务响应的信号
	unios_task_t id;				///< 任务ID
	uint8_t data[4];   				///< 任务数据体，实际长度另行给出
} OSTaskSignal;

#pragma pack(pop)

/// 计算 taskStruct 类型的任务结构除 data 外的开销
#define OSTaskCost(taskStruct)				(sizeof(taskStruct) - sizeof(((taskStruct*)0)->data))
/// 计算 taskStruct 类型的任务结构中数据长度为 dataLen 时，对应的 shift_block_t 数据长度
#define OSTaskBlockLen(taskStruct, dataLen) (dataLen + OSTaskCost(taskStruct))
/// 计算 shift_block_t 为 taskStruct 类型时，任务结构中的数据长度
#define OSTaskDataLen(taskStruct, block)    (SBDataLen(block) - OSTaskCost(taskStruct))

/// @brief forward 声明: 处理各类任务
/// @param dataLen 任务净荷数据长度
/// @return 是否需要释放此任务
static bool handleRxMessage(OSTaskRxMessage* msg, uint32_t dataLen);
static bool handleTxMessage(OSTaskTxMessage* msg, uint32_t dataLen);
static bool handleTxMessageXn(OSTaskTxMessageXn* msg, uint32_t dataLen);
static bool handleSignalTask(OSTaskSignal* task, uint32_t dataLen);
static bool handleMsTimerTask(OSTaskMsTimer* task, uint32_t dataLen);
static bool handleRtcTimerTask(OSTaskRtcTimer* task, uint32_t dataLen);

/// 下一个任务ID(=0表示无效)
static uintptr_t nextTaskId = 0;

/// 主任务队列
static shift_block_queue_t* mainTaskQueue = NULL;

/// 分配任务
#define unios_task_alloc(taskType, taskStruct, dataLen, errReturn) \
	shift_block_t* block = (shift_block_t*)SBQAlloc(mainTaskQueue, taskType, OSTaskBlockLen(taskStruct, dataLen)); \
	if(!block) return errReturn; \
	taskStruct* task = (taskStruct*)SBData(block);	


//=============================================================================
// RX 消息处理
//=============================================================================

std_err_t unios_task_dispatch_rx_msg(unios_task_rxmsg_type_t msgType, void* receiver, size_t len, void* data) {
    // 分配任务
	unios_task_alloc(UNIOS_TASK_RX, OSTaskRxMessage, len, STD_ERR_NO_MEM);
	// 填写任务数据
	task->receiver = receiver;
	task->msgType = msgType;
	if(len && data) memcpy(task->data, data, len);	
	// 提交任务
	return SBQCommit(mainTaskQueue, block) ? STD_ERR_OK : STD_ERR_FAIL;
}

/// @brief 接收消息处理器登记表
static unios_task_rxmsg_handler_map_t *_rxmsgmap = NULL;

std_err_t unios_task_register_rxmsg_handler(unios_task_rxmsg_type_t msgType, unios_task_rxmsg_handler_t handler, void *cookie) {
	unios_task_rxmsg_handler_reg_t* item;
	int emptyIdx = -1;

	// 遍历登记表，看此类消息是否已经登记过处理器
	for(size_t i=0; i < _rxmsgmap->count; i++) {
		item = _rxmsgmap->items + i;
		if(item->msgType == msgType) {
			// 此类消息是否已经登记过处理器，覆盖之间的登记项 !
			item->handler = handler;
			item->cookie  = cookie;
			return STD_ERR_OK;
		}
		// 记录第一个空位
		if(item->handler == NULL && emptyIdx < 0) emptyIdx = i;
	}
	
	// 如果没有空位了，则返回错误
	if(emptyIdx < 0) return STD_ERR_NO_RESOURCES;
	
	// 在空位置登记新的处理器
	item = _rxmsgmap->items + emptyIdx;
	item->msgType = msgType;
	item->handler = handler;
	item->cookie  = cookie;
	return STD_ERR_OK;
}

void unios_task_unregister_rxmsg_handler(unios_task_rxmsg_handler_t handler, void *cookie) {
	unios_task_rxmsg_handler_reg_t* item;
	for(size_t i=0; i < _rxmsgmap->count; i++) {
		item = _rxmsgmap->items + i;
		if(item->handler == handler && item->cookie == cookie) {
			item->msgType = 0; item->handler = NULL; item->cookie = NULL;
		}
	}
}

void unios_task_unregister_rxmsg_type(unios_task_rxmsg_type_t msgType) {
	unios_task_rxmsg_handler_reg_t* item;
	for(size_t i=0; i < _rxmsgmap->count; i++) {
		item = _rxmsgmap->items + i;
		if(item->msgType == msgType) {
			item->msgType = 0; item->handler = NULL; item->cookie = NULL;
		}
	}
}

static bool handleRxMessage(OSTaskRxMessage* msg, uint32_t dataLen) {
	const unios_task_rxmsg_handler_reg_t* item = NULL;

	// 根据消息类型，查找消息处理器
	for(size_t i=0; i < _rxmsgmap->count; i++) {
		item = _rxmsgmap->items + i;
		// 如果处理器的消息类型为 0，表示可以处理所有消息
		if(item->handler && (item->msgType == 0 || item->msgType == msg->msgType)) {
			// 调用消息处理函数
			item->handler(item->cookie, msg->receiver, dataLen, msg->data);
			break;
		}
	}

	// 不管消息有没有正确处理，都要释放消息
	return true;
}


//=============================================================================
// TX 消息处理
//=============================================================================

std_err_t unios_task_dispatch_tx_msg(transceiver_t* sender, size_t len, const void* data) {
	if(!sender || !len || !data) return STD_ERR_INVALID_ARG;	
    // 分配任务
	unios_task_alloc(UNIOS_TASK_TX, OSTaskTxMessage, len, STD_ERR_NO_MEM);
	// 填写任务数据
	task->sender = sender;
	if(len && data) memcpy(task->data, data, len);	
	// 提交任务
	return SBQCommit(mainTaskQueue, block) ? STD_ERR_OK : STD_ERR_FAIL;
}

static bool handleTxMessage(OSTaskTxMessage* msg, uint32_t dataLen) {
	// 调用发送接口发送数据
	if(msg->sender) msg->sender->tx(msg->sender, msg->data, dataLen, NULL);	
	// 发送完，删除它
	return true;
}

unios_task_t unios_task_dispatch_tx_msg_xn(transceiver_t* sender, uint32_t interval, uint32_t repeat, size_t len, const void* data, bool fireFirstNow) {
	if(!sender || !len || !data || !repeat || (repeat == 1 && fireFirstNow)) return NULL;
    
	// 分配任务
	unios_task_alloc(UNIOS_TASK_TXN, OSTaskTxMessageXn, len, 0);
	// 填写任务数据
	task->sender = sender;
	task->interval = interval;
	task->repeat = repeat;
	task->lastSent = unios_ms_now();
	task->id = (unios_task_t)(++nextTaskId); // 下一个任务 ID
	if(len && data) memcpy(task->data, data, len);	
	
	// 提交任务
	if(!SBQCommit(mainTaskQueue, block)) { return NULL; }
	
	// 如果需要就立即发送第一次
	if(fireFirstNow) handleTxMessageXn(task, len);
	return task->id; // 返回任务 ID
}

static bool handleTxMessageXn(OSTaskTxMessageXn* msg, uint32_t dataLen) {
	if((unios_ms_elapsed(msg->lastSent) > msg->interval) && (msg->repeat > 0)) {
		// 更新最近一次发送时间
		msg->lastSent = unios_ms_now();
		// 如果不是永远发送，则发送次数减1
		if(msg->repeat != UNIOS_TASK_REPEAT_FOREVER) { msg->repeat--; }		
		// 调用发送接口发送数据
		if(msg->sender) msg->sender->tx(msg->sender, msg->data, dataLen, NULL);	
	}

	// 如果所有次数已经发送完，删除它
	return (msg->repeat == 0);
}


//=============================================================================
// Signal 任务处理
//=============================================================================

/// 信号存储变量
static uint32_t osSignal = 0;
/// 信号访问临界区
static unios_critical_section_t osSignalCritical;

std_err_t unios_task_fire_signal(unios_signal_t signal) {
	unios_critical_section_enter(osSignalCritical, 1000);
	osSignal |= BV(signal);
	unios_critical_section_leave(osSignalCritical);
	return STD_ERR_OK;
}

unios_task_t unios_task_run_on_signal(unios_signal_t signal, unios_task_handler_t handler, size_t len, const void* data) {
	if(signal >= UNIOS_SIGNAL_MAX || !handler) return NULL;
	
    // 分配任务
	unios_task_alloc(UNIOS_TASK_SIGNAL, OSTaskSignal, len, 0);
	// 填写任务数据
	task->handler = handler;
	task->signal = signal;
	task->id = (unios_task_t)(++nextTaskId); // 下一个任务 ID
	if(len && data) memcpy(task->data, data, len);
	
	// 提交任务
	if(!SBQCommit(mainTaskQueue, block)) { return NULL; }
	return task->id; // 返回任务 ID
}

static bool handleSignalTask(OSTaskSignal* task, uint32_t dataLen) {
	uint32_t mask = BV(task->signal);
	// 判断指定的信号是否已经产生
	if(!(mask & osSignal)) { return false; }
	
    // 执行任务回调
	if(task->handler) { task->handler(dataLen, task->data); }
	
	// 执行完成之后清除信号
	unios_critical_section_enter(osSignalCritical, 1000);
	osSignal &= (~mask);
	unios_critical_section_leave(osSignalCritical);
	
    // 信号响应任务，执行完后不应删除，应该由应用层自行决定何时删除任务！
	return false;
}


//=============================================================================
// MsTimer 任务处理
//=============================================================================

unios_task_t unios_task_run_every(uint32_t interval, uint32_t repeat, unios_task_handler_t handler, size_t len, const void* data, bool fireFirstNow) {
    // 分配任务
	unios_task_alloc(UNIOS_TASK_MS_TIMER, OSTaskMsTimer, len, 0);
	// 填写任务数据
	task->handler = handler;
	task->interval = interval;
	task->lastActive = fireFirstNow ? 0 : unios_ms_now();
	task->repeat = repeat;
	task->id = (unios_task_t)(++nextTaskId); // 下一个任务 ID
	if(len && data) memcpy(task->data, data, len);
	
	// 提交任务
	if(!SBQCommit(mainTaskQueue, block)) { return NULL; }
	
	// 如果需要就立即执行一次
	if(fireFirstNow) handleMsTimerTask(task, len);
	return task->id; // 返回任务 ID
}

static bool handleMsTimerTask(OSTaskMsTimer* task, uint32_t dataLen) {
	if((unios_ms_elapsed(task->lastActive) > task->interval) && (task->repeat > 0)) {
		// 更新最近一次执行时间为当前时间
		task->lastActive = unios_ms_now();
		// 如果不是永远执行，则执行次数减1
		if(task->repeat != UNIOS_TASK_REPEAT_FOREVER) { task->repeat--; }
        // 执行任务回调
        if(task->handler) { task->handler(dataLen, task->data); }
	}

	// 如果所有次数已经执行完，删除它
	return (task->repeat == 0);
}

void unios_task_reset_timer(unios_task_t taskId) {
	if(!taskId) { return; }
	shift_block_t *block = NULL;
	while((block = SBQNext(mainTaskQueue, block)) != NULL) {
		// 只有毫秒定时任务才可以复位
		if(SBType(block) == UNIOS_TASK_MS_TIMER && ((OSTaskMsTimer*)SBData(block))->id == taskId) {
			((OSTaskMsTimer*)SBData(block))->lastActive = unios_ms_now(); return;
		}
	}
}


//=============================================================================
// RTC Timer 任务处理
//=============================================================================

unios_task_t unios_task_run_at(unios_time_t rtc_clock, uint32_t repeat, unios_task_handler_t handler, size_t len, const void* data) {
    // 分配任务
	unios_task_alloc(UNIOS_TASK_RTC_TIMER, OSTaskRtcTimer, len, 0);
	// 填写任务数据
	task->handler = handler;
	task->time = time2seconds(&rtc_clock);
	task->lastActive = 0;
	task->repeat = repeat;
	task->id = (unios_task_t)(++nextTaskId); // 下一个任务 ID
	if(len && data) memcpy(task->data, data, len);
	
	// 提交任务
	if(!SBQCommit(mainTaskQueue, block)) { return NULL; }
	return task->id; // 返回任务 ID
}

static bool handleRtcTimerTask(OSTaskRtcTimer* task, uint32_t dataLen) {
	// 如果上次执行是在 24 小时之内，则不执行
	if(unios_rtcsec_get() - task->lastActive <= 24*60*60UL) return false;
	long diff = unios_rtcsec_in_day() - task->time;
	if(ABS(diff) < 5 && (task->repeat > 0)) {
		// 更新最近一次执行时间为当前时间
		task->lastActive = unios_rtcsec_get();
		// 如果不是永远执行，则执行次数减1
		if(task->repeat != UNIOS_TASK_REPEAT_FOREVER) { task->repeat--; }
        // 执行任务回调
        if(task->handler) { task->handler(dataLen, task->data); }
	}

	// 如果所有次数已经执行完，删除它
	return (task->repeat == 0);
}


//=============================================================================
// 任务系统管理
//=============================================================================

void unios_task_kill(unios_task_t taskId) {
	if(!taskId) { return; }
	shift_block_t *block = NULL;
	while((block = SBQNext(mainTaskQueue, block)) != NULL) {
		// 注意: 不是所有的任务类型都有任务 ID !!!
		if( ((SBType(block) == UNIOS_TASK_TXN      ) && (((OSTaskTxMessageXn*)SBData(block))->id == taskId)) ||
			((SBType(block) == UNIOS_TASK_MS_TIMER ) && (((OSTaskMsTimer    *)SBData(block))->id == taskId)) ||
			((SBType(block) == UNIOS_TASK_SIGNAL   ) && (((OSTaskSignal     *)SBData(block))->id == taskId)) ||
		    ((SBType(block) == UNIOS_TASK_RTC_TIMER) && (((OSTaskRtcTimer   *)SBData(block))->id == taskId))
		) {
			SBFree(block);
			return;
		}
	}
}

void unios_task_clear_all(void) { 
	SBQClear(mainTaskQueue); 
}

std_err_t unios_task_system_init(shift_block_queue_t *taskqueue, unios_task_rxmsg_handler_map_t *rxmsgmap) {
	if(!taskqueue) return STD_ERR_INVALID_ARG;
	if(!taskqueue->css) unios_critical_section_init(&taskqueue->css);
	mainTaskQueue = taskqueue;
	_rxmsgmap = rxmsgmap;
	unios_task_clear_all();
	return STD_ERR_OK;
}

void unios_task_system_loop_step(void) {
	shift_block_t *block = NULL;
	bool needFree = false; // 任务处理完成后是否需要删除

	while(true) {
		// 取下一个任务
		block = SBQNext(mainTaskQueue, block);
		if(!block) {
			// 没有更多任务了，清除任务队列中的无效任务，并压缩任务队列内存空间
			// 注意：在此之前删除的所有任务内存空间会被释放！原来的任务指针不再可用！！！
			SBQPack(mainTaskQueue);
			return;
		}

		// !!! 每个任务执行前，先喂一次看门狗 !!!
		unios_watchdog_feed();

		// !!! 锁定任务: 处理完成后如果不再使用，则一定要在任务函数中解锁，否则不能释放!!!
		SBLock(block); // {{{

		switch(SBType(block)) {
			case UNIOS_TASK_RX:        needFree = handleRxMessage   ( (OSTaskRxMessage  *)SBData(block), OSTaskDataLen(OSTaskRxMessage  , block) ); break;
			case UNIOS_TASK_TX:        needFree = handleTxMessage   ( (OSTaskTxMessage  *)SBData(block), OSTaskDataLen(OSTaskTxMessage  , block) ); break;
			case UNIOS_TASK_TXN:       needFree = handleTxMessageXn ( (OSTaskTxMessageXn*)SBData(block), OSTaskDataLen(OSTaskTxMessageXn, block) ); break;
			case UNIOS_TASK_SIGNAL:    needFree = handleSignalTask  ( (OSTaskSignal     *)SBData(block), OSTaskDataLen(OSTaskSignal     , block) ); break;
			case UNIOS_TASK_MS_TIMER:  needFree = handleMsTimerTask ( (OSTaskMsTimer    *)SBData(block), OSTaskDataLen(OSTaskMsTimer    , block) ); break;
			case UNIOS_TASK_RTC_TIMER: needFree = handleRtcTimerTask( (OSTaskRtcTimer   *)SBData(block), OSTaskDataLen(OSTaskRtcTimer   , block) ); break;
			default:                   needFree = true; // 不知如何处理的任务，要删除掉
		}

		// 给需要删除的任务做标记
		if(needFree) SBFree(block);
		// !!! 不管是否需要删除，执行完后都需要解锁 !!!
		SBUnlock(block); // }}}
	}
}
